#include "test_NEON.h"

/**
 * todo 所有的都转换成了 uint8_t 类型，直接用 jbyte 呢？
 */

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT void JNICALL
Java_com_andova_libyuv_TestNEON_00024Companion_TestNEON_1VTRN(JNIEnv *env, jobject thiz, jbyteArray src_u, jbyteArray src_v, jbyteArray dst_uv, jint stride) {
    jbyte *u_src = env->GetByteArrayElements(src_u, nullptr);
    jbyte *v_src = env->GetByteArrayElements(src_v, nullptr);
    jbyte *uv_dst = env->GetByteArrayElements(dst_uv, nullptr);
#if 0
    jint len_u = env->GetArrayLength(src_u);
    jint len_v = env->GetArrayLength(src_v);
    jint len = len_u > len_v ? len_v : len_u;
    for (int i = 0; i <= len / 8; ++i) {
        jbyte *pos_u = u_src + i * 8;
        jbyte *pos_v = v_src + i * 8;
        jbyte *pos_dst = uv_dst + i * 2 * 8;
        asm volatile(
        "exec:                   \n"
        "vld1.8 d0,[%0]         \n"
        "vld1.8 d1,[%1]         \n"
        "vtrn.8 d0,d1           \n"
        "vst1.8 {d0,d1},[%2]    \n"
        :"+r"(pos_u),           // %0
        "+r"(pos_v),            // %1
        "+r"(pos_dst)           // %2
        :
        : "memory", "q0"
        );
    }
#else
    // 一定要转化为uint8_t,很重要,jbyte是int8_t
    auto u_src_t = (uint8_t *) u_src;
    auto v_src_t = (uint8_t *) v_src;
    auto uv_dst_t = (uint8_t *) uv_dst;
    asm volatile(
    "1:                                         \n"
    "vld1.8     q0, [%0]!                       \n" // load 16 U
    "vld1.8     q1, [%1]!                       \n" // load 16 V
    "vtrn.8     q0, q1                          \n" // transpose U&V
    "subs       %3, %3, #32                     \n" // 32 processed per loop
    "vst1.8     q0, [%2]!                       \n" // store 8 pairs of UV
    "bgt        1b                              \n"
    : "+r"(u_src_t),                                // %0
    "+r"(v_src_t),                                  // %1
    "+r"(uv_dst_t),                                 // %2
    "+r"(stride)                                    // %3  // Output registers
    :                                               // Input registers
    : "cc", "memory", "q0", "q1"  // Clobber List
    );
#endif
    env->ReleaseByteArrayElements(src_u, u_src, 0);
    env->ReleaseByteArrayElements(src_v, v_src, 0);
    env->ReleaseByteArrayElements(dst_uv, uv_dst, 0);
}

JNIEXPORT void JNICALL
Java_com_andova_libyuv_TestNEON_00024Companion_TestHalfMerge(JNIEnv *env, jobject thiz, jbyteArray src_u, jbyteArray src_v, jbyteArray dst_uv, jint stride) {
    jbyte *u_src = env->GetByteArrayElements(src_u, nullptr);
    jbyte *v_src = env->GetByteArrayElements(src_v, nullptr);
    jbyte *uv_dst = env->GetByteArrayElements(dst_uv, nullptr);
#if false
    libyuv::HalfMergeUVPlane((uint8_t *) u_src, SUBSAMPLE(stride, 2),
                             (uint8_t *) v_src, SUBSAMPLE(stride, 2),
                             (uint8_t *) uv_dst, stride,
                             stride, 1);
#elif false
    libyuv::HalfMergeUVRow_NEON((uint8_t *) u_src, 0,
                                (uint8_t *) v_src, 0,
                                (uint8_t *) uv_dst, stride);
#elif false
    const uint8_t *src_u_t = (uint8_t *) u_src;
    const uint8_t *src_v_t = (uint8_t *) v_src;
    const uint8_t *dst_uv_t = (uint8_t *) uv_dst;
    asm volatile(
    "1:                                        \n"
    "vld1.8      {q0}, [%0]!                   \n"  // load 16 U values
    "vld1.8      {q1}, [%2]!                   \n"  // load 16 V values
    "vld1.8      {q2}, [%1]!                   \n"
    "vld1.8      {q3}, [%3]!                   \n"
    "vpaddl.u8   q0, q0                        \n"  // half size
    "vpaddl.u8   q1, q1                        \n"
    "vpadal.u8   q0, q2                        \n"
    "vpadal.u8   q1, q3                        \n"
//    #define EXEC
#ifdef EXEC
    "vqrshrn.u16 d0, q0, #2                    \n"
    "vqrshrn.u16 d1, q1, #2                    \n"
#else
//    "vshl.u16       q0, q0, #8      \n" // <<
//    "vqrshrn.u16    d0, q0, #8      \n" // half size and >>
#endif
    "subs        %5, %5, #16                   \n"  // 16 src pixels per loop
#ifdef EXEC
    "vst2.8      {d0, d1}, [%4]!               \n"  // store 8 UV pixels
#undef EXEC
#else
    "vst1.8      q0, [%4]!               \n"  // store 8 UV pixels
#endif
    "bgt         1b                            \n"
    : "+r"(src_u_t),    // %0
    "+r"(src_u_t),  // %1
    "+r"(src_v_t),    // %2
    "+r"(src_v_t),  // %3
    "+r"(dst_uv_t),   // %4
    "+r"(stride)     // %5
    :
    : "cc", "memory", "q0", "q1", "q2", "q3");
#else
    const uint8_t *src_u_t = (uint8_t *) u_src;
    const uint8_t *src_v_t = (uint8_t *) v_src;
    const uint8_t *dst_uv_t = (uint8_t *) uv_dst;
    asm volatile(
    "1:                             \n"
    "vld1.8         q0, [%0]!       \n" // load 16 U
    "vld1.8         q1, [%1]!       \n" // load 16 V
    "vshl.u16       q0, q0, #8      \n" // <<
    "vqrshrn.u16    d0, q0, #8      \n" // half size and >>
    "vshl.u16       q1, q1, #8      \n" // <<
    "vqrshrn.u16    d1, q1, #8      \n" // half size and >>
    "subs           %3, %3, #16     \n" // 16 processed per loop
    #if true
    "vst2.8         {d0, d1}, [%2]! \n" // store 8 pairs of UV
    #else
    "vst1.8         q0, [%2]! \n"       // store 8 pairs of UV
    #endif
    "bgt            1b              \n"
    : "+r"(src_u_t),                    // %0
    "+r"(src_v_t),                      // %1
    "+r"(dst_uv_t),                     // %2
    "+r"(stride)                        // %3  // Output registers
    :                                   // Input registers
    : "cc", "memory", "q0", "q1"        // Clobber List
    );
#endif
    env->ReleaseByteArrayElements(src_u, u_src, 0);
    env->ReleaseByteArrayElements(src_v, v_src, 0);
    env->ReleaseByteArrayElements(dst_uv, uv_dst, 0);
}

JNIEXPORT void JNICALL
Java_com_andova_libyuv_TestNEON_00024Companion_TestNEON_1VPADDL(JNIEnv *env, jobject thiz,
                                                               jbyteArray src_u, jbyteArray src_v,
                                                               jbyteArray dst_u, jbyteArray dst_v,
                                                               jint stride) {
    jbyte *buf_src_u = env->GetByteArrayElements(src_u, nullptr);
    jbyte *buf_src_v = env->GetByteArrayElements(src_v, nullptr);
    jbyte *buf_dst_u = env->GetByteArrayElements(dst_u, nullptr);
    jbyte *buf_dst_v = env->GetByteArrayElements(dst_v, nullptr);

    auto src_u_t = (uint8_t *) buf_src_u;
    auto src_v_t = (uint8_t *) buf_src_v;
    auto dst_u_t = (uint8_t *) buf_dst_u;
    auto dst_v_t = (uint8_t *) buf_dst_v;
#if true
    asm volatile(
    "exec:                  \n"
    "vld1.8     q0, [%0]    \n"
    "vld1.8     q1, [%1]    \n"
    "vpaddl.u8  q0, q0      \n"
    "vpaddl.u8  q1, q1      \n"
    "vst1.8     q0, [%2]    \n"
    "vst1.8     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
    "+r"(dst_u_t),          // %2
    "+r"(dst_v_t)           // %3
    :
    : "memory", "q0", "q1"
    );
#else
    asm volatile(
    "exec:                  \n"
    "vld1.8     q0, [%0]    \n"
    "vld1.8     q1, [%1]    \n"
    "vpaddl.u8  q0, q1      \n"
    "vst1.8     q0, [%2]    \n"
    "vst1.8     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
    "+r"(dst_u_t),          // %2
    "+r"(dst_v_t)           // %3
    :
    : "memory", "q0", "q1"
    );
#endif

    env->ReleaseByteArrayElements(src_u, buf_src_u, 0);
    env->ReleaseByteArrayElements(src_v, buf_src_v, 0);
    env->ReleaseByteArrayElements(dst_u, buf_dst_u, 0);
    env->ReleaseByteArrayElements(dst_v, buf_dst_v, 0);
}

JNIEXPORT void JNICALL
Java_com_andova_libyuv_TestNEON_00024Companion_TestNEON_1VPADAL(JNIEnv *env, jobject thiz,
                                                               jbyteArray src_u, jbyteArray src_v,
                                                               jbyteArray dst_u, jbyteArray dst_v,
                                                               jint stride) {
    jbyte *buf_src_u = env->GetByteArrayElements(src_u, nullptr);
    jbyte *buf_src_v = env->GetByteArrayElements(src_v, nullptr);
    jbyte *buf_dst_u = env->GetByteArrayElements(dst_u, nullptr);
    jbyte *buf_dst_v = env->GetByteArrayElements(dst_v, nullptr);

    auto src_u_t = (uint8_t *) buf_src_u;
    auto src_v_t = (uint8_t *) buf_src_v;
    auto dst_u_t = (uint8_t *) buf_dst_u;
    auto dst_v_t = (uint8_t *) buf_dst_v;

    asm volatile(
    "exec1:                  \n"
    "vld1.8     q0, [%0]    \n"
    "vld1.8     q1, [%1]    \n"
    #if false
    "vpadal.s8  q0, q0      \n"
    "vpadal.s8  q1, q1      \n"
    "vst1.8     q0, [%2]    \n"
    "vst1.8     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
    #elif false
    "vpadal.u8  q0, q1      \n"
    "vst1.8     q0, [%2]    \n"
    "vst1.8     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
    #elif false
    "vpaddl.u8  q0, q0      \n"
    "vpadal.u8  q0, q1      \n"
    "vst1.8     q0, [%2]    \n"
    "vst1.8     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_u_t),          // %1
    #elif true
    "vpaddl.u8      q0, q0      \n"
    "vpadal.u8      q0, q1      \n"
    "vqrshrn.u16    d0, q0, #2  \n" // half size
    "vst1.8         q0, [%2]    \n"
    "vst1.8         q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_u_t),          // %1
#else
    "vld1.16     q0, [%0]    \n"
    "vld1.16     q1, [%1]    \n"
    "vpadal.s16  q0, q1      \n"
    "vst1.16     q0, [%2]    \n"
    "vst1.16     q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
#endif
    "+r"(dst_u_t),          // %2
    "+r"(dst_v_t)           // %3
    :
    : "memory", "q0", "q1"
    );

    env->ReleaseByteArrayElements(src_u, buf_src_u, 0);
    env->ReleaseByteArrayElements(src_v, buf_src_v, 0);
    env->ReleaseByteArrayElements(dst_u, buf_dst_u, 0);
    env->ReleaseByteArrayElements(dst_v, buf_dst_v, 0);
}


JNIEXPORT void JNICALL
Java_com_andova_libyuv_TestNEON_00024Companion_TestNEON_1VQRSHRN(JNIEnv *env, jobject thiz,
                                                                jbyteArray src_u, jbyteArray src_v,
                                                                jbyteArray dst_u, jbyteArray dst_v,
                                                                jint stride) {
    jbyte *buf_src_u = env->GetByteArrayElements(src_u, nullptr);
    jbyte *buf_src_v = env->GetByteArrayElements(src_v, nullptr);
    jbyte *buf_dst_u = env->GetByteArrayElements(dst_u, nullptr);
    jbyte *buf_dst_v = env->GetByteArrayElements(dst_v, nullptr);

    auto src_u_t = (uint8_t *) buf_src_u;
    auto src_v_t = (uint8_t *) buf_src_v;
    auto dst_u_t = (uint8_t *) buf_dst_u;
    auto dst_v_t = (uint8_t *) buf_dst_v;
#if true
    asm volatile(
    "exec2:                      \n"
    "vld1.8         q0, [%0]    \n"
    "vld1.8         q1, [%1]    \n"
    "vshl.u16       q0, q0, #8  \n" // <<
    "vqrshrn.u16    d0, q0, #8  \n" // half size and >>
    "vqrshrn.u16    d2, q1, #2  \n"
    "vst1.8         q0, [%2]    \n"
    "vst1.8         q1, [%3]    \n"
    :"+r"(src_u_t),         // %0
    "+r"(src_v_t),          // %1
    "+r"(dst_u_t),          // %2
    "+r"(dst_v_t)           // %3
    :
    : "memory", "q0", "q1"
    );
#else
    asm volatile(
    "exec2:                     \n"
    "vld1.8         q0, [%0]    \n"
    "vqrshrn.u16    d0, q0, #1  \n" // half size
    "vst1.8         q0, [%1]    \n"
    :"+r"(src_v_t),         // %0
    "+r"(dst_v_t)          // %1
    :
    : "memory", "q0", "q1"
    );
#endif

    env->ReleaseByteArrayElements(src_u, buf_src_u, 0);
    env->ReleaseByteArrayElements(src_v, buf_src_v, 0);
    env->ReleaseByteArrayElements(dst_u, buf_dst_u, 0);
    env->ReleaseByteArrayElements(dst_v, buf_dst_v, 0);
}

#ifdef __cplusplus
}
#endif